# -*- coding: utf-8 -*-
# @Time : 2022/3/17 14:29
# @FileName: MyTableView.py
# @Mail: lizhigen1996@aliyun.com

__author__ = 'Zhigen.li'

import typing
from PyQt5.QtCore import QItemSelectionModel, pyqtSignal, Qt, QModelIndex
from PyQt5.QtGui import QStandardItemModel, QStandardItem, QBrush, QColor, QFont, QCursor
from PyQt5.QtWidgets import QTableView, QAbstractItemView, QItemDelegate, QHeaderView, QAbstractSlider, QMenu, QAction, QMessageBox, QWidget
from Tools.Util import *
from threading import Thread
from math import ceil
from UI.Curve.YieldRateDialog import YieldRateDialog
from UI.Curve.MonthDataCurve import *
from UI.Curve.HoldingPie import *
import os
import json
from Config.Config import config, CatchExceptions


# 只读代理
class OnlyReadDelegate(QItemDelegate):
    def __init__(self, parent):
        super(OnlyReadDelegate, self).__init__(parent)

    def createEditor(self, QWidget, QStyleOptionViewItem, QModelIndex):
        return None

class MyTableView(QTableView, QAbstractSlider):
    fundTabDataSignal = pyqtSignal(list)
    fundNumSignal = pyqtSignal(list)
    fundSateChangedSignal = pyqtSignal()
    curveCloseBtnClicked = pyqtSignal(bool)
    showCurveSignal = pyqtSignal()
    monthDataSignal = pyqtSignal(int, str, str, list, list, list, list, list, list)

    def __init__(self, parent, curveWidget: QWidget, dialogs, fundMode='all'):
        super(MyTableView, self).__init__(parent)
        self.frozenTableView = QTableView(self)
        # self.horizontalView = QTableView(self)
        self.up = True
        self.curveWidget = curveWidget
        self.dialogs = dialogs

        self.fundMode = fundMode
        model = QStandardItemModel(0, 18, self)
        selectionModel = QItemSelectionModel(model, self)
        self.setModel(model)
        self.init()
        self.horizontalHeader().setStretchLastSection(True)
        self.setSelectionModel(selectionModel)

        self.setSelectionBehavior(QAbstractItemView.SelectRows)  # 选择整行
        self.setSelectionMode(QAbstractItemView.SingleSelection)  # 只能选择一行
        self.setContextMenuPolicy(Qt.CustomContextMenu)  # 可弹出右键菜单
        # self.setEditTriggers(QTableView.NoEditTriggers)  # 不可编辑
        # self.verticalHeader().hide()  # 隐藏行名

        model.setHorizontalHeaderItem(0, QStandardItem('基金代码'))
        model.setHorizontalHeaderItem(1, QStandardItem('基金简称'))
        model.setHorizontalHeaderItem(2, QStandardItem('拼音简写'))
        model.setHorizontalHeaderItem(3, QStandardItem('日期'))
        model.setHorizontalHeaderItem(4, QStandardItem('单位净值'))
        model.setHorizontalHeaderItem(5, QStandardItem('累计净值'))
        model.setHorizontalHeaderItem(6, QStandardItem('日增长率'))
        model.setHorizontalHeaderItem(7, QStandardItem('近一周'))
        model.setHorizontalHeaderItem(8, QStandardItem('近1月'))
        model.setHorizontalHeaderItem(9, QStandardItem('近3月'))
        model.setHorizontalHeaderItem(10, QStandardItem('近6月'))
        model.setHorizontalHeaderItem(11, QStandardItem('近1年'))
        model.setHorizontalHeaderItem(12, QStandardItem('近2年'))
        model.setHorizontalHeaderItem(13, QStandardItem('近3年'))
        model.setHorizontalHeaderItem(14, QStandardItem('今年来'))
        model.setHorizontalHeaderItem(15, QStandardItem('成立来'))
        model.setHorizontalHeaderItem(16, QStandardItem('成立日期'))
        model.setHorizontalHeaderItem(17, QStandardItem('手续费'))

        # 设置只读
        self.setItemDelegate(OnlyReadDelegate(self))
        if self.fundMode == 'all':
            self.fundPage = 1
        else:
            self.fundPage = 0
        self.allDataNum = 0

        # 选中穴位改变信号
        self.selectionModel().currentChanged.connect(self.onCurrentRowChanged)
        self.clicked.connect(self.onClicked)
        self.frozenTableView.clicked.connect(self.onClicked)
        # 滚动条滚动
        self.verticalScrollBar().valueChanged.connect(self.onVerticalScrollBarChanged)
        self.fundTabDataSignal.connect(self.setTabData)
        self.monthDataSignal.connect(self.onMonthData)

        # 首页爬取信息
        if self.fundMode == 'all':
            fundDataTh = Thread(target=self.getTabFirstPageData)
            fundDataTh.setDaemon(True)
            fundDataTh.start()

        # 添加右击菜单
        self.popMenu = QMenu(self)
        self.addAction = QAction()
        self.addAction.setText('添加自选')
        self.addAction.triggered.connect(self.addSelfSelection)
        # self.detailsAction = QAction()
        # self.detailsAction.setText('详细信息')

        self.navAction = QAction()
        self.navAction.setText('月初净值月末净值对比')
        self.navAction.triggered.connect(self.onMonthDataActionTriggered)
        self.perAction = QAction()
        self.perAction.setText('月最高涨、最低跌收益百分比')
        self.perAction.triggered.connect(self.onMonthDataActionTriggered)
        self.fluAction = QAction()
        self.fluAction.setText('月波动值（最高涨、最低跌之差）')
        self.fluAction.triggered.connect(self.onMonthDataActionTriggered)
        self.difAction = QAction()
        self.difAction.setText('月净值差值(月末减月初，该月是否盈亏）')
        self.difAction.triggered.connect(self.onMonthDataActionTriggered)

        self.holdAction = QAction()
        self.holdAction.setText('持仓占比')
        self.holdAction.triggered.connect(self.onHoldActionTriggered)

        self.popMenu.addAction(self.addAction)
        self.popMenu.addAction(self.holdAction)
        self.popMenu.addAction(self.navAction)
        self.popMenu.addAction(self.perAction)
        self.popMenu.addAction(self.fluAction)
        self.popMenu.addAction(self.difAction)
        # self.popMenu.addAction(self.detailsAction)

        # 右键点击事件
        self.customContextMenuRequested.connect(self.slotContextMenu)

    def slotContextMenu(self, pos):
        index = self.indexAt(pos)
        if index:
            self.popMenu.exec(QCursor.pos())

    # 加自选
    @CatchExceptions
    def addSelfSelection(self, b):
        index = self.currentIndex()
        fundCodeIdx = self.model().index(index.row(), 0)
        fundCode = self.model().data(fundCodeIdx)
        name = self.model().data(self.model().index(index.row(), 1))
        funds = config.getFund()
        if len(funds) > config.getMaxFundsCount() or len(funds) == config.getMaxFundsCount():
            QMessageBox.warning(self, '当前自选基金过多', '当前自选基金过多，目前仅支持显示 {} 自选。请删除多余自选！'.format(config.getMaxFundsCount()))
            return
        data = fundAccurate(fundCode)
        if not config.addFund(data):
            QMessageBox.warning(self, '自选列表已存在该基金', '自选列表已存在该基金{}，请勿重复添加！'.format(fundCode))
        else:
            self.fundSateChangedSignal.emit()
            QMessageBox.information(self, '添加自选基金成功', '添加自选基金成功！{} {}'.format(fundCode, name))

    @CatchExceptions
    def onHoldActionTriggered(self, b):
        index = self.currentIndex()
        fundCodeIdx = self.model().index(index.row(), 0)
        fundCode = self.model().data(fundCodeIdx)
        name = self.model().data(self.model().index(index.row(), 1))
        targetNames, urls, targetSecids, rates, correlationNames, correlationUrls = equityHoldings(fundCode)
        holdingPie = HoldingPie()
        holdingPie.create_piechart(name, fundCode, targetNames, targetSecids, rates)

    @CatchExceptions
    def onMonthDataActionTriggered(self, b):
        if self.sender() == self.navAction:
            mode = 1
        elif self.sender() == self.perAction:
            mode = 2
        elif self.sender() == self.fluAction:
            mode = 3
        else:
            mode = 4
        index = self.currentIndex()
        fundCodeIdx = self.model().index(index.row(), 0)
        fundCode = self.model().data(fundCodeIdx)
        name = self.model().data(self.model().index(index.row(), 1))

        t = Thread(target=self.monthDataThread, args=[mode, name, fundCode])
        t.start()

    @CatchExceptions
    def monthDataThread(self, mode, name, code):
        mlist, startlist, endlist, maxlist, minlist, reslist = fundMonthData(code)
        self.monthDataSignal.emit(mode, name, code, mlist, startlist, endlist, maxlist, minlist, reslist)

    def onMonthData(self, mode, name, code, mlist, startlist, endlist, maxlist, minlist, reslist):
        if mode == 1:
            m = MonthDataCurve_NavBar(name, code)
            m.showCurve(mlist, startlist, endlist)
        elif mode == 2:
            m = MonthDataCurve_PerBar(name, code)
            m.showCurve(mlist, maxlist, minlist)
        elif mode == 3:
            m = MonthDataCurve_FluBar(name, code)
            m.showCurve(mlist, maxlist, minlist)
        else:
            m = MonthDataCurve_DifBar(name, code)
            m.showCurve(mlist, reslist)

    @CatchExceptions
    def onClicked(self, index: QModelIndex):
        print(index.row(), index.column(), self.model().data(index))
        fundCodeIdx = self.model().index(index.row(), 0)
        fundCode = self.model().data(fundCodeIdx)
        name = self.model().data(self.model().index(index.row(), 1))

        typeCode = ''
        title = ''
        if index.column() == 8:
            typeCode = 'm'
            title = '{}近一个月累计收益率走势'.format(name)
        elif index.column() == 9:
            typeCode = 'q'
            title = '{}近三个月累计收益率走势'.format(name)
        elif index.column() == 10:
            typeCode = 'hy'
            title = '{}近六个月累计收益率走势'.format(name)
        elif index.column() == 11:
            typeCode = 'y'
            title = '{}近一年累计收益率走势'.format(name)
        elif index.column() == 12:
            typeCode = 'twy'
            title = '{}近两年累计收益率走势'.format(name)
        elif index.column() == 13:
            typeCode = 'try'
            title = '{}近三年累计收益率走势'.format(name)
        elif index.column() == 14:
            typeCode = 'sy'
            title = '{}今年来累计收益率走势'.format(name)
        elif index.column() == 15:
            typeCode = 'se'
            title = '{}成立来累计收益率走势'.format(name)

        if typeCode:
            self.showCurveSignal.emit()
            data = fundYieldRate(fundCode, typeCode)
            dialog = YieldRateDialog()
            dialog.curveCloseBtnClicked.connect(self.curveCloseBtnClicked)
            dialog.showPlot(data, title)
            if len(self.dialogs) > 0:
                self.curveWidget.layout().removeWidget(self.dialogs[0])
                del self.dialogs[0]

            self.curveWidget.layout().addWidget(dialog)
            # self.curveWidget.resize(100, 1400)
            self.dialogs.append(dialog)

    def showEvent(self, a0):
        if self.fundPage == 0 and self.fundMode != 'all':
            self.onVerticalScrollBarChanged(0)

    def setAllDataNum(self, num):
        self.allDataNum = num

    @CatchExceptions
    def getTabFirstPageData(self):
        numList = []
        for data in fundrankingFirstPage(self.fundMode, numList):
            if self.allDataNum == 0:
                self.allDataNum = numList[0]
                self.fundNumSignal.emit(numList)
            self.fundTabDataSignal.emit(data)

    def onCurrentRowChanged(self, current: QModelIndex, previous: QModelIndex):
        # 选中的cell发生改变
        pass

    @CatchExceptions
    def onVerticalScrollBarChanged(self, value: int):
        # 滚动到最底部
        if value == self.verticalScrollBar().maximum():
            # 每页50个数据
            if self.fundPage < ceil(self.allDataNum * 1.0 / 50.0):
                self.fundPage += 1
                # 线程爬取数据
                fundDataTh = Thread(target=self.getTabNextPageData)
                fundDataTh.setDaemon(True)
                fundDataTh.start()

    @CatchExceptions
    def getTabNextPageData(self):
        for data in fundrankingNextPage(self.fundMode, self.fundPage):
            self.fundTabDataSignal.emit(data)

    @CatchExceptions
    def setTabData(self, data):
        model = self.model()
        rowNum = self.model().rowCount()

        for idx in range(len(data)):
            model.setItem(rowNum, idx, QStandardItem(data[idx]))
            model.item(rowNum, idx).setTextAlignment(Qt.AlignCenter)

            if idx == 0 or idx == 1 or idx == 17:
                model.item(rowNum, idx).setForeground(QBrush(QColor(0, 52, 151)))
                font = model.item(rowNum, idx).font()
                font.setUnderline(True)  # 字体增加下划线
                model.item(rowNum, idx).setFont(font)

            if idx in range(6, 16):
                if '%' in data[idx]:
                    num = data[idx]
                    num = num.replace('%', '')
                    num = float(num)
                    if num > 0:
                        model.item(rowNum, idx).setForeground(QBrush(QColor(220, 0, 0)))
                    elif num < 0:
                        model.item(rowNum, idx).setForeground(QBrush(QColor(9, 124, 37)))
            if idx in range(4, 16):
                model.item(rowNum, idx).setFont(QFont('Microsoft YaHei', 9, 75))  # 字体加粗

    def init(self):
        self.frozenTableInit()
        # self.horizontalViewInit()

        # self.horizontalHeader().sectionResized.connect(self.updateSectionWidth)
        self.verticalHeader().sectionResized.connect(self.updateSectionHeight)
        self.verticalScrollBar().valueChanged.connect(self.vConnectFV)
        self.frozenTableView.verticalScrollBar().valueChanged.connect(self.fVConnectV)
        # self.horizontalScrollBar().valueChanged.connect(self.hConnectH)

    def vConnectFV(self, a0: int):
        self.viewport().stackUnder(self.frozenTableView)
        # self.frozenTableView.stackUnder(self.horizontalView)
        self.frozenTableView.verticalScrollBar().setValue(a0)

    def fVConnectV(self, a0: int):
        self.viewport().stackUnder(self.frozenTableView)
        # self.frozenTableView.stackUnder(self.horizontalView)
        self.verticalScrollBar().setValue(a0)

    # def hConnectH(self, a0: int):
    #     self.viewport().stackUnder(self.horizontalView)
    #     self.horizontalView.stackUnder(self.frozenTableView)
    #     self.horizontalView.horizontalScrollBar().setValue(a0)

    def frozenTableInit(self):
        self.frozenTableView.setModel(self.model())
        self.frozenTableView.verticalHeader().hide()
        self.frozenTableView.setFocusPolicy(Qt.NoFocus)
        self.frozenTableView.horizontalHeader().setFixedHeight(self.horizontalHeader().height())
        self.frozenTableView.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed)

        self.viewport().stackUnder(self.frozenTableView)
        self.frozenTableView.setStyleSheet('QTableView {'
                                           'border: none;'
                                           'background-color: #8EDE21;'
                                           'selection-background-color: #999}')
        self.frozenTableView.setSelectionModel(self.selectionModel())
        [self.frozenTableView.setColumnHidden(col, True) for col in range(2, self.model().columnCount())]
        self.frozenTableView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.frozenTableView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.frozenTableView.show()

        self.updateFrozenTableGeometry()
        self.frozenTableView.setVerticalScrollMode(self.ScrollPerPixel)
        self.setVerticalScrollMode(self.ScrollPerPixel)
        self.setHorizontalScrollMode(self.ScrollPerPixel)

    def horizontalViewInit(self):
        self.horizontalView.setModel(self.model())
        self.horizontalView.horizontalHeader().hide()
        self.horizontalView.setFocusPolicy(Qt.NoFocus)
        self.horizontalView.verticalHeader().setFixedWidth(self.verticalHeader().width())
        self.horizontalView.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)

        self.frozenTableView.stackUnder(self.horizontalView)
        self.horizontalView.setStyleSheet('QTableView { border: none;'
                                          'background-color: #8EDE21;'
                                          'selection-background-color: #999}')
        self.horizontalView.setSelectionModel(self.selectionModel())
        [self.horizontalView.setRowHidden(row, True) for row in range(1, self.model().rowCount())]
        self.horizontalView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.horizontalView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.horizontalView.show()

        self.updateFrozenTableGeometry()
        self.horizontalView.setHorizontalScrollMode(self.ScrollPerPixel)

    def updateFrozenTableGeometry(self):
        self.frozenTableView.setGeometry(self.verticalHeader().width() + self.frameWidth(),
                                         self.frameWidth(), self.columnWidth(0) + self.columnWidth(1),
                                         self.viewport().height() + self.horizontalHeader().height())
        # self.horizontalView.setGeometry(self.frameWidth(), self.frameWidth() + self.horizontalHeader().height(),
        #                                 self.viewport().width() + self.verticalHeader().width(), self.rowHeight(0))

    def updateSectionWidth(self, logicalIndex, oldSize, newSize):
        self.horizontalView.setColumnWidth(logicalIndex, newSize)
        if not logicalIndex:
            self.frozenTableView.setColumnWidth(0, newSize)
        self.updateFrozenTableGeometry()

    def updateSectionHeight(self, logicalIndex, oldSize, newSize):
        self.frozenTableView.setRowHeight(logicalIndex, newSize)
        if not logicalIndex:
            self.horizontalView.setRowHeight(0, newSize)
        self.updateFrozenTableGeometry()

    def resizeEvent(self, e) -> None:
        QTableView.resizeEvent(self, e)
        self.updateFrozenTableGeometry()

    def scrollTo(self, index, hint: QAbstractItemView.ScrollHint = ...) -> None:
        if index.column() > 0 or index.row() > 0:
            QTableView.scrollTo(self, index, hint)

    def moveCursor(self, cursorAction, modifiers):
        current = QTableView.moveCursor(self, cursorAction, modifiers)
        if cursorAction == QAbstractItemView.MoveLeft and current.column() > 0 \
                and self.visualRect(current).topLeft().x() < self.frozenTableView.columnWidth(0):
            newValue = self.verticalScrollBar().value() + self.visualRect(current).topLeft().x() \
                       - self.frozenTableView.columnWidth(0)
            self.horizontalScrollBar().setValue(newValue)
        # if cursorAction == QAbstractItemView.MoveUp and current.row() > 0 \
        #         and self.visualRect(current).topLeft().y() < self.horizontalView.rowHeight(0):
        #     newValue = self.horizontalScrollBar().value() + self.visualRect(current).topLeft().y() \
        #                - self.horizontalView.rowHeight(0)
        #     self.verticalScrollBar().setValue(newValue)

        return current

